import os
import math
import platform
import vtk
from threading import *
from trame.app import get_server
from trame.ui.vuetify import VAppLayout
from trame.widgets import vtk as vtkTrame, vuetify, html, vega, trame
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkFiltersCore import (
    vtkAppendPolyData,
)
from vtkmodules.vtkRenderingCore import vtkInteractorStyle
from vtkmodules.vtkFiltersHybrid import vtkPolyDataSilhouette
from GLTFLoader import GLTFLoader
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderer,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkProperty,
    vtkPropPicker,
    vtkPicker,
    vtkCellPicker,
    vtkCompositePolyDataMapper,
)
import datetime
import asyncio
import nest_asyncio
import altair as alt
import pandas as pd
# 不能删，删了运行不了
import vtkmodules.vtkRenderingOpenGL2  # noqa
# 日志
import logging
# 监控cpu、内存、gpu的使用情况
import psutil
# 监控gpu
import GPUtil
nest_asyncio.apply()

# 日志配置
# logging.basicConfig(filename='log.log', level=logging.DEBUG,
#                     format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

VIEW_INTERACT = [
    {"button": 1, "action": "Rotate"},
    {"button": 2, "action": "Pan"},
    {"button": 3, "action": "Zoom", "scrollEnabled": True},
    {"button": 1, "action": "Pan", "alt": True},
    {"button": 1, "action": "Zoom", "control": True},
    {"button": 1, "action": "Pan", "shift": True},
    {"button": 1, "action": "Roll", "alt": True, "shift": True},
]
# 计算总点数，总面数
# total_points = 0
# total_cells = 0
# total_lines = 0
# 鼠标位置
mouse = [0, 0]
# 是否右键点击
right_click_menu = 0
wheel_count = 0
DEFAULT_POSITION = 56
# 是否有动画
animation = False
# 动画时长
duration = 0
# 动画时间戳
timestamp = None
# 动画步数
step = 0.0
# 读取文件数量
fileNum = 0

data = {
    'Quarter': ['Q1', 'Q2', 'Q3', 'Q4'],
    'Product1': [150, 200, 300, 400],
    'Product2': [50, 150, 250, 200],
    'Product3': [100, 250, 150, 300]
}

df = pd.DataFrame(data)
df_long = df.melt('Quarter', var_name='Pr', value_name='Sales')
chart = alt.Chart(df_long).mark_bar().encode(
    x='Quarter:O',  # O = ordinal data type for categorical order
    y='Sales:Q',  # Q = quantitative data type for numerical values
    color='Pr:N',  # N = nominal data type for categorical labels
    order=alt.Order(
      # Sort the segments of the bars by this field
      'Pr:N',
      sort='ascending'
    )
).properties(
    title='Product Sales by Quarter',
    width=200
)

# 创建示例数据(也是叠堆图)
data_two = pd.DataFrame({
    'Category': ['A', 'B', 'C', 'D'],
    'Value1': [10, 15, 12, 8],
    'Value2': [5, 8, 7, 10],
    'Value3': [8, 6, 11, 9]
})

# 创建叠堆柱状图
chart_two = alt.Chart(data_two).transform_fold(
    fold=['Value1', 'Value2', 'Value3'],
    as_=['Variable', 'Value']
).mark_bar().encode(
    x='Category:N',
    y='sum(Value):Q',
    color='Variable:N'
)

server = get_server()
server.client_type = "vue2"
state = server.state
state.x = 0
state.y = 0
state.z = 0
state.coneDisplay = {"display": "none"}
state.current_pickername = "undefined"
state.current_framerate = -1
state.total_points = 0
state.total_lines = 0
state.total_cells = 0
state.menupositionx = 458
state.menupositiony = 329
state.current_actor0 = -1
state.modelList = []
state.readerList = ["vtkGLTFImporter", "vtkGLTFReader"]
state.currentReader = "vtkGLTFImporter"
state.selectedFile = 0
state.animation = True
state.animationSpeed = 1.0
state.animationDuration = - 1
state.colour_temperature = 100
state.animationList = []  # 用于记录模型的动画列表
state.animation_index = 0  # 用于记录当前动画的index，根据index启动动画
state.oldAnimation_index = 0  # 用于记录上一个动画的index下，根据index停止动画
state.showAnimationSelect = False  # 用于判断是否显示动画下拉框
state.devicePixelRatio = 1  # 用于获取客户端的像素比，右键功能时用到
# 16进制转rgba


def hex_to_rgba(hex_color):
    hex_color = hex_color.lstrip('#')
    hlen = len(hex_color)
    if hlen == 8:
        r = float(int(hex_color[0:2], 16))/255
        g = float(int(hex_color[2:4], 16))/255
        b = float(int(hex_color[4:6], 16))/255
        a = float(int(hex_color[6:8], 16))/255
        return (r, g, b, a)
    elif hlen == 6:
        r = float(int(hex_color[0:2], 16))/255
        g = float(int(hex_color[2:4], 16))/255
        b = float(int(hex_color[4:6], 16))/255
        return (r, g, b, 1.0)
    else:
        raise ValueError('Invalid hex color: ' + hex_color)


def convert_K_to_RGB(colour_temperature):
    # range check
    if colour_temperature < 1000:
        colour_temperature = 1000
    elif colour_temperature > 40000:
        colour_temperature = 40000

    tmp_internal = colour_temperature / 100.0

    # red
    if tmp_internal <= 66:
        red = 255
    else:
        tmp_red = 329.698727446 * math.pow(tmp_internal - 60, -0.1332047592)
        if tmp_red < 0:
            red = 0
        elif tmp_red > 255:
            red = 255
        else:
            red = tmp_red

    # green
    if tmp_internal <= 66:
        tmp_green = 99.4708025861 * math.log(tmp_internal) - 161.1195681661
        if tmp_green < 0:
            green = 0
        elif tmp_green > 255:
            green = 255
        else:
            green = tmp_green
    else:
        tmp_green = 288.1221695283 * math.pow(tmp_internal - 60, -0.0755148492)
        if tmp_green < 0:
            green = 0
        elif tmp_green > 255:
            green = 255
        else:
            green = tmp_green

    # blue
    if tmp_internal >= 66:
        blue = 255
    elif tmp_internal <= 19:
        blue = 0
    else:
        tmp_blue = 138.5177312231 * \
            math.log(tmp_internal - 10) - 305.0447927307
        if tmp_blue < 0:
            blue = 0
        elif tmp_blue > 255:
            blue = 255
        else:
            blue = tmp_blue

    return red, green, blue


# 找到当前组件序号
def get_index(actor):
    actor_collection = renderer.GetActors()
    for i in range(actor_collection.GetNumberOfItems()):
        if (actor == actor_collection.GetItemAsObject(i)):
            return i
    return -1

# 设置透明度


def setOpacity(option):
    if (state.current_actor0 >= 0):
        actor_collection = renderer.GetActors()
        actor = actor_collection.GetItemAsObject(state.current_actor0)
        if option == 'show':
            actor.GetProperty().SetOpacity(0.4)
        else:
            actor.GetProperty().SetOpacity(1)

# 隐藏当前组件


def hide_actor():
    if (state.current_actor0 >= 0):
        actor_collection = renderer.GetActors()
        actor = actor_collection.GetItemAsObject(state.current_actor0)
        actor.VisibilityOff()
    # 隐藏菜单栏
    with state:
        state.tooltipStyle = {"display": "none"}
    ctrl.view_update()

# 删除所有组件


def clear_all():
    renderer.RemoveAllViewProps()
    # 隐藏菜单栏
    with state:
        state.tooltipStyle = {"display": "none"}
    ctrl.view_update()

# 显示所有组件


def show_all():
    actor_collection = renderer.GetActors()
    for i in range(actor_collection.GetNumberOfItems()):
        actor = actor_collection.GetItemAsObject(i)
        actor.VisibilityOn()
    # 隐藏菜单栏
    with state:
        state.tooltipStyle = {"display": "none"}
    ctrl.view_update()


def imageClick(k, s):
    global fileNum
    global wheel_count
    tempArry = []
    wheel_count = 0
    for i in range(fileNum):
        if (state.modelList[i]["name"] == k["name"]):
            # 对象类型必须解构赋值，否则无法更新界面
            tempArry.append({"name": state.modelList[i]["name"], "style": {
                            "border": "1px solid red"}, "file_name": state.modelList[i]["file_name"]})
            new_file_name = state.modelList[i]["file_name"]
            state.selectedFile = i
        else:
            tempArry.append({"name": state.modelList[i]["name"], "style": {
            }, "file_name": state.modelList[i]["file_name"]})

    with state:
        state.modelList = tempArry
    if (state.currentReader == "vtkGLTFImporter"):
        load_file_importer(new_file_name)
    elif (state.currentReader == "vtkGLTFReader"):
        load_file_reader(new_file_name)
    elif (state.currentReader == "gltfLoader"):
        load_file_loader(new_file_name)
    restCamera()
    # renderer.ResetCameraScreenSpace()


def restart_animation():
    global step
    step = 0.0


def load_file_importer(file_name):
    global reader
    global animation
    reader = None
    renderer.RemoveAllViewProps()
    reader = vtk.vtkGLTFImporter()
    reader.SetFileName(file_name)
    reader.SetRenderWindow(renderWindow)
    reader.Update()
    if (reader.GetNumberOfAnimations() > 0):
        animation = True
    else:
        animation = False

    state.total_points = 0
    state.total_lines = 0
    state.total_cells = 0
    total_points = 0
    total_lines = 0
    total_cells = 0
    actor_collection = renderer.GetActors()
    for i in range(actor_collection.GetNumberOfItems()):
        actor = actor_collection.GetItemAsObject(i)
        total_points += actor.GetMapper().GetInput().GetNumberOfPoints()
        data = actor.GetMapper().GetInput()
        for i in range(data.GetNumberOfCells()):
            cell = data.GetCell(i)
            cellType = cell.GetCellType()
            if cellType == vtk.VTK_LINE or cellType == vtk.VTK_POLY_LINE:
                total_lines += 1
            if cellType in [vtk.VTK_TRIANGLE, vtk.VTK_QUAD, vtk.VTK_POLYGON]:
                total_cells += 1

    state.total_points = total_points
    state.total_lines = total_lines
    state.total_cells = total_cells
    print("loading finished")
    renderWindow.Render()
    ctrl.view_update()


def load_file_reader(file_name):
    global reader
    global animation
    global duration
    reader = None
    renderer.RemoveAllViewProps()
    reader = vtk.vtkGLTFReader()
    reader.SetFileName(file_name)
    reader.ApplyDeformationsToGeometryOn()
    reader.UpdateInformation()
    tempAnimationList = []
    state.showAnimationSelect = False
    if (reader.GetNumberOfAnimations() > 0):
        state.showAnimationSelect = True
        state.animation_index = 0
        reader.EnableAnimation(state.animation_index)
        duration = reader.GetAnimationDuration(state.animation_index)
        for i in range(reader.GetNumberOfAnimations()):
            tempAnimationList.append(i)
        state.animationList = tempAnimationList

    reader.UpdateInformation()

    mapper = vtkCompositePolyDataMapper()
    mapper.SetInputConnection(reader.GetOutputPort())

    actor = vtkActor()
    actor.SetMapper(mapper)
    renderer.AddActor(actor)
    if (reader.GetNumberOfAnimations() > 0):
        animation = True
    else:
        animation = False
    print("loading finished")
    renderWindow.Render()
    ctrl.view_update()


def load_file_loader(file_name):
    global reader
    global animation
    global duration
    reader = None
    renderer.RemoveAllViewProps()

    reader = GLTFLoader()
    reader.load(file_name)
    renderer.AddActor(reader.scenes[0])
    try:

        if len(reader.animations):
            animation = True
            state.showAnimationSelect = True
            state.animation_index = 0
            # reader.EnableAnimation(state.animation_index)
            duration = reader.animations[state.animation_index].GetEndTime()
            for i in range(len(reader.animations)):
                tempAnimationList.append(i)
            state.animationList = tempAnimationList
        else:
            animation = False
        print("loading finished")

        renderWindow.Render()
        ctrl.view_update()
    except Exception as e:
        # 处理其他类型的异常
        print("发生了错误：", e)

# 隐藏当前组件


def restCamera():
    # 获取当前相机
    camera = renderer.GetActiveCamera()

    # 重置相机的位置和方向为初始状态
    camera.SetPosition(initial_position)
    camera.SetFocalPoint(initial_focal_point)
    camera.SetViewUp(initial_view_up)
    renderer.ResetCamera()
    ctrl.view_update()

# 鼠标控制


class MouseInteractorHighLightActor(vtkInteractorStyleTrackballCamera):
    selectColor = hex_to_rgba('#00e0ff')

    def __init__(self, parent=None):
        self.AddObserver("LeftButtonPressEvent", self.onLeftButtonDown)
        self.AddObserver("RightButtonPressEvent", self.onRightButtonDown)
        self.AddObserver("MouseWheelBackwardEvent", self.OnMouseWheelBackward)
        self.AddObserver("MouseWheelForwardEvent", self.OnMouseWheelForward)
        self.LastPickedActor = None
        self.LastPickedColor = None
        self.NewPickedActor = None

        self.LastPickedNode = None  # 石老师的选中方法用到
        # 石老师所写的外轮廓边缘描绘示例代码
        silhouette = vtkPolyDataSilhouette()
        silhouette.SetCamera(renderer.GetActiveCamera())

        # Create mapper and actor for silhouette
        silhouetteMapper = vtkPolyDataMapper()
        silhouetteMapper.SetInputConnection(silhouette.GetOutputPort())

        silhouetteActor = vtkActor()
        silhouetteActor.SetMapper(silhouetteMapper)
        silhouetteActor.GetProperty().SetColor(1, 1, 0.5)
        silhouetteActor.GetProperty().SetLineWidth(3)

        self.Silhouette = silhouette
        self.SilhouetteActor = silhouetteActor

    # 鼠标左键
    def onLeftButtonDown(self, obj, event):
        # 只要右键菜单栏打开，无论点击何处均应该关闭
        global right_click_menu
        if (right_click_menu == 1):
            right_click_menu = 0
            with state:
                state.tooltipStyle = {"display": "none"}

        # 精准拾取
        clickPos = self.GetInteractor().GetEventPosition()

        # 石老师给的gltfLoader精准拾取不通用，需要欢方法
        if (state.currentReader == "gltfLoader"):
            print('石老师的方法')
            #  Pick from this location.
            picker = vtkCellPicker()
            node = None
            actor = None

            if picker.Pick(clickPos[0], clickPos[1], 0.0, renderer) == 1:
                path = picker.GetPath()  # assembly path
                actor = path.GetLastNode().GetViewProp()
                path.DeleteLastNode()
                node = path.GetLastNode().GetViewProp()

            if node:
                if node != self.LastPickedNode:
                    if self.LastPickedNode:
                        self.LastPickedNode.RemovePart(self.SilhouetteActor)
                    self.LastPickedNode = node
                    self.LastPickedNode.AddPart(self.SilhouetteActor)

                if actor != self.LastPickedActor:
                    self.LastPickedActor = actor
                    self.Silhouette.SetCamera(renderer.GetActiveCamera())
                    self.Silhouette.SetInputData(actor.GetMapper().GetInput())
            elif self.LastPickedNode:
                self.LastPickedNode.RemovePart(self.SilhouetteActor)
                self.LastPickedNode = None
        else:
            proppicker = vtkPropPicker()
            picker = vtkPicker()
            proppicker.Pick(clickPos[0], clickPos[1],
                            0, self.GetDefaultRenderer())
            picker.Pick(clickPos[0], clickPos[1], 0, self.GetDefaultRenderer())
            # 更新NewPickedActor
            actor_collection = picker.GetActors()
            number_of_actors = actor_collection.GetNumberOfItems()
            self.NewPickedActor = proppicker.GetActor()
            # 点击在组件上
            if actor_collection.GetNumberOfItems() > 0 and self.NewPickedActor != None:
                state.current_actor0 = get_index(self.NewPickedActor)
                # 如果此前点击过组件，需要把组件颜色重新赋值
                if self.LastPickedActor:
                    self.LastPickedActor.GetProperty().SetColor(self.LastPickedColor)
                # 将组件属性储存在LastPickedProperty中
                self.LastPickedColor = self.NewPickedActor.GetProperty().GetColor()
                # 组件染色

                self.NewPickedActor.GetProperty().SetColor(
                    self.selectColor[0], self.selectColor[1], self.selectColor[2])

                # 如果前一次与这一次点击了相同的组件，则将组件染回原来的颜色
                if (self.LastPickedActor == self.NewPickedActor):
                    self.NewPickedActor.GetProperty().SetColor(self.LastPickedColor)
                    self.NewPickedActor = vtkActor()
                    state.current_actor0 = -1
                # 更新LastPickedActor
                self.LastPickedActor = self.NewPickedActor
        self.OnLeftButtonDown()
        ctrl.view_update()
        return

    # 鼠标右键
    def onRightButtonDown(self, obj, event):

        # 打开菜单栏
        global right_click_menu
        if (right_click_menu == 0):
            right_click_menu = 1
            mouse = interactor.GetEventPosition()
            with state:
                state.menupositionx = mouse[0]
                state.menupositiony = mouse[1]
                state.tooltipStyle = {
                    "position": "absolute",
                    "left": f"{(mouse[0] * state.devicePixelRatio )}px",
                    "bottom": f"{(mouse[1]* state.devicePixelRatio )}px",
                    "zIndex": 9999,
                }
        else:
            right_click_menu = 0
            with state:
                state.tooltipStyle = {"display": "none"}
        # self.OnRightButtonDown()
        return

    # 鼠标滚轮向后
    def OnMouseWheelBackward(self, obj, event):
        global wheel_count
        wheel_count -= 1
        # show_all()
        if wheel_count <= 0:
            setOpacity('none')
        return super().OnMouseWheelBackward()

    # 鼠标滚轮向前
    def OnMouseWheelForward(self, obj, event):
        global wheel_count
        wheel_count += 1
        if wheel_count >= 3:
            setOpacity('show')

        return super().OnMouseWheelForward()


CURRENT_DIRECTORY = os.path.abspath(os.path.dirname(__file__))

user_input = "model/"

# 结合 CURRENT_DIRECTORY 和 user_input 形成新的地址
new_directory = os.path.join(CURRENT_DIRECTORY, user_input)

# 在新地址中搜索 .glb 文件
if platform.system() == "Windows":
    file_names_glb = [os.path.join('./model', f)
                      for f in os.listdir('./model') if f.endswith(".glb")]
else:
    file_names_glb = [os.path.join(new_directory, f) for f in os.listdir(
        new_directory) if f.endswith(".glb")]

fileNum = len(file_names_glb)

# -----------------------------------------------------------------------------
# VTK pipeline
# -----------------------------------------------------------------------------

renderer = vtkRenderer()
# renderer.SetBackground(	84.0, 255.0, 159.0)
bg_color = hex_to_rgba('#132c4a')
renderer.SetBackground(bg_color[0], bg_color[1], bg_color[2])

# 启用雾化效果
renderer.UseDepthPeelingOn()
renderer.SetMaximumNumberOfPeels(100)
renderer.SetOcclusionRatio(0.9)

light = vtk.vtkLight()

# 创建一个vtkRenderWindow对象
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)

# 创建一个vtkRenderWindowInteractor对象
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renderWindow)


# 创建一个vtkRenderer对象
file_name_initial = file_names_glb[0]

# 创建一个vtkGLTFImporter对象
reader = vtk.vtkGLTFImporter()
if platform.system() == "Windows":
    file_name_initial = file_name_initial.replace("\\", "/")

reader.SetFileName(file_name_initial)
reader.SetRenderWindow(renderWindow)
reader.Update()

print(reader.GetNumberOfAnimations(), "动画数量")

if (reader.GetNumberOfAnimations() > 0):
    animation = True
    tempAnimationList = []
    state.showAnimationSelect = True
    for i in range(reader.GetNumberOfAnimations()):
        tempAnimationList.append(i)
    state.animationList = tempAnimationList

actor_collection = renderer.GetActors()
for i in range(actor_collection.GetNumberOfItems()):
    actor = actor_collection.GetItemAsObject(i)
    state.total_points += actor.GetMapper().GetInput().GetNumberOfPoints()
    data = actor.GetMapper().GetInput()
    for i in range(data.GetNumberOfCells()):
        cell = data.GetCell(i)
        cellType = cell.GetCellType()
        if cellType == vtk.VTK_LINE or cellType == vtk.VTK_POLY_LINE:
            state.total_lines += 1
        if cellType in [vtk.VTK_TRIANGLE, vtk.VTK_QUAD, vtk.VTK_POLYGON]:
            state.total_cells += 1

print("appending finished")

# 设置交互器样式
style = MouseInteractorHighLightActor()
style.SetDefaultRenderer(renderer)
interactor.SetInteractorStyle(style)

# 添加灯光
renderer.AddLight(light)

interactor.Initialize()

renderWindow.Render()
defaultFrameRate = renderWindow.GetDesiredUpdateRate()
# 打印默认渲染帧数
print("默认渲染帧数：", defaultFrameRate)
renderWindow.SetDesiredUpdateRate(30)


async def log_performance():
    await asyncio.sleep(60)

    cpu_percent = psutil.cpu_percent(1)
    # logging.debug(f"CPU 使用率：{cpu_percent}%")

    memory = psutil.virtual_memory()
    memory_percent = memory.percent
    # logging.debug(f"内存使用率：{memory_percent}%")

    gpus = GPUtil.getGPUs()
    for gpu in gpus:
        print(f"GPU{gpu.id} 使用率：{gpu.load * 100}%")
        # logging.debug(f"GPU{gpu.id} 使用率：{gpu.load * 100}%")


async def async_framerate():
    while True:
        # 这个await会造成页面卡顿一秒钟，在生产环境中不应该启用该功能，否则会影响操作
        await asyncio.sleep(1)

        with state:
            state.current_framerate = 1.0 / \
                renderWindow.GetRenderers().GetFirstRenderer().GetLastRenderTimeInSeconds()


async def async_animation():
    global animation
    global reader
    cur_reader = state.currentReader
    global duration
    global timestamp
    global step
    while (True):
        if (cur_reader != state.currentReader):
            print("reader changed", state.currentReader)
            cur_reader = state.currentReader
            if (cur_reader == "vtkGLTFImporter"):
                load_file_importer(
                    state.modelList[state.selectedFile]["file_name"])
            elif (cur_reader == "vtkGLTFReader"):
                load_file_reader(
                    state.modelList[state.selectedFile]["file_name"])
            elif (cur_reader == "gltfLoader"):
                load_file_loader(
                    state.modelList[state.selectedFile]["file_name"])
        if (animation and state.animation):
            if (cur_reader == "vtkGLTFImporter"):
                if (state.animationDuration > 0):
                    tmp_duration = state.animationDuration
                else:
                    tmp_duration = 2
                while step < state.animationSpeed * tmp_duration:
                    if (animation == False):
                        step = 0.0
                        break
                    if (cur_reader != state.currentReader):
                        step = 0.0
                        break
                    if (state.animation == False):
                        break
                    await asyncio.sleep(0.01)
                    reader.Modified()
                    # reader.EnableAnimation(state.animation_index)
                    reader.UpdateTimeStep(step / state.animationSpeed)
                    step += 1
                    ctrl.view_update()
                if (state.animation != False):
                    step = 0.0
            elif (cur_reader == "vtkGLTFReader"):
                # 留存代码
                # while True:
                #     await asyncio.sleep(0.1)
                #     if (animation == False):
                #         break
                #     if (cur_reader != state.currentReader):
                #         break
                #     if (state.animation == False):
                #         break
                #     if (state.animationDuration > 0):
                #         tmp_duration = state.animationDuration
                #     else:
                #         tmp_duration = duration
                #     now = datetime.datetime.now()
                #     if timestamp is None:
                #         timestamp = now
                #     elapsed = (now - timestamp).total_seconds()
                #     if elapsed > tmp_duration:
                #         timestamp = now
                #     reader.UpdateTimeStep(elapsed)
                #     reader.Modified()
                #     renderWindow.Render()
                #     ctrl.view_update()
                if (state.animationDuration > 0):
                    tmp_duration = state.animationDuration
                else:
                    tmp_duration = duration
                while step < state.animationSpeed*tmp_duration:

                    if (animation == False):
                        step = 0.0
                        break
                    if (cur_reader != state.currentReader):
                        step = 0.0
                        break
                    if (state.animation == False):
                        break
                    await asyncio.sleep(0.01)
                    reader.UpdateTimeStep(step / state.animationSpeed)
                    reader.Modified()
                    renderWindow.Render()
                    step += 1.0
                    ctrl.view_update()
                if (state.animation != False):
                    step = 0.0
            elif (cur_reader == "gltfLoader"):
                if (state.animationDuration > 0):
                    tmp_duration = state.animationDuration
                else:
                    tmp_duration = duration
                while step < state.animationSpeed*tmp_duration:

                    if (animation == False):
                        step = 0.0
                        break
                    if (cur_reader != state.currentReader):
                        step = 0.0
                        break
                    if (state.animation == False):
                        break
                    await asyncio.sleep(0.01)
                    try:
                        reader.animations[state.animation_index].SetAnimationTime(
                            step / state.animationSpeed)
                    except Exception as e:
                        # 处理其他类型的异常
                        print("石老师的动画发生了错误：", e)

                    renderWindow.Render()
                    step += 1.0
                    ctrl.view_update()
                if (state.animation != False):
                    step = 0.0
            else:
                raise ValueError('Invalid reader type: ' + cur_reader)
        else:
            await asyncio.sleep(1)
            renderWindow.Render()
            ctrl.view_update()

# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------

if (fileNum > 0):
    for file_name in file_names_glb:
        if platform.system() == "Windows":
            file_name = file_name.replace("\\", "/")
        state.modelList.append({"name": os.path.basename(
            file_name), "style": {}, "file_name": file_name})
    state.modelList[0]["style"] = {"border": "1px solid red"}
state.update(
    {"tooltip": "",
     "tooltipStyle": {"display": "none"},
     "coneVisibility": False,
     "interactorSettings": VIEW_INTERACT, })
ctrl = server.controller
renderWindow.Render()

# -----------------------------------------------------------------------------

# 模型读取器切换


@state.change("selectedReader")
def update_currentReader(selectedReader, **kwargs):
    state.current_actor0 = -1
    state.currentReader = selectedReader
    # 添加处理方法，先清空当前模型后重新读取
    ctrl.view_update()

# 控制灯开关


@state.change("open")
def update_open(open, **kwargs):
    state.open = open
    if open:
        renderer.SetAutomaticLightCreation(1)
        renderer.RemoveAllLights()
    else:
        renderer.SetAutomaticLightCreation(0)
        renderer.RemoveAllLights()
        renderer.AddLight(light)
    ctrl.view_update()

# 灯光强度


@state.change("strength")
def update_strength(strength, **kwargs):
    state.strength = strength
    light.SetIntensity(strength)
    ctrl.view_update()

# 色温


@state.change("colour_temperature")
def update_colour_temperature(colour_temperature, **kwargs):
    state.colour_temperature = colour_temperature
    r, g, b = convert_K_to_RGB(colour_temperature)
    light.SetColor(float(r)/255.0, float(g)/255.0, float(b)/255.0)
    light.SetDiffuseColor(float(r)/255.0, float(g)/255.0, float(b)/255.0)
    ctrl.view_update()

# 灯光颜色


@state.change("color")
def update_light_color(color, **kwargs):
    state.lightColor = color
    if (len(state.lightColor) >= 8):
        color_rgba = hex_to_rgba(color)
        light.SetColor(color_rgba[0], color_rgba[1], color_rgba[2])
        light.SetDiffuseColor(color_rgba[0], color_rgba[1], color_rgba[2])
        ctrl.view_update()

# 灯光类型


@state.change("light_type")
def update_light_type(light_type, **kwargs):
    state.lightType = light_type
    if light_type == "平行光":
        light.SetLightTypeToSceneLight()
        light.PositionalOff()
        light.SetPosition(float(state.x), float(state.y), float(state.z))
        state.coneDisplay = {"display": "none"}
    elif light_type == "头灯":
        light.SetLightTypeToHeadlight()
        state.coneDisplay = {"display": "none"}
    elif light_type == "相机灯源":
        light.SetLightTypeToCameraLight()
        state.coneDisplay = {"display": "none"}
    else:
        light.SetLightTypeToSceneLight()
        light.PositionalOn()
        light.SetFocalPoint(renderer.GetActiveCamera().GetFocalPoint())
        light.SetConeAngle(10)
        light.SetPosition(float(state.x), float(state.y), float(state.z))
        state.coneDisplay = {}
    ctrl.view_update()

# 灯光x坐标


@state.change("x_position")
def update_x(x_position, **kwargs):
    try:
        if (isinstance(float(x_position), float)):
            state.x = x_position
            if state.lightType == "平行光" or state.lightType == "点光源":
                light.SetPosition(float(state.x), float(
                    state.y), float(state.z))
    except (ValueError):
        print("x_position is not float")
    ctrl.view_update()

# 灯光y坐标


@state.change("y_position")
def update_y(y_position, **kwargs):
    try:
        if (isinstance(float(y_position), float)):
            state.y = y_position
            if state.lightType == "平行光" or state.lightType == "点光源":
                light.SetPosition(float(state.x), float(
                    state.y), float(state.z))
    except (ValueError):
        print("y_position is not float")
    ctrl.view_update()

# 灯光z坐标


@state.change("z_position")
def update_z(z_position, **kwargs):
    try:
        if (isinstance(float(z_position), float)):
            state.z = z_position
            if state.lightType == "平行光" or state.lightType == "点光源":
                light.SetPosition(float(state.x), float(
                    state.y), float(state.z))
    except (ValueError):
        print("z_position is not float")
    ctrl.view_update()

# 灯光张角


@state.change("coneangle")
def update_coneangle(coneangle, **kwargs):
    state.coneangle = coneangle
    if state.lightType == "点光源":
        light.SetConeAngle(coneangle)
    ctrl.view_update()

# 动画开关


@state.change("animation")
def update_animation(animation, **kwargs):
    state.animation = animation
    ctrl.view_update()

# 设置动画


@state.change("animation_index")
def update_animation_index(animation_index, **kwargs):
    reader.DisableAnimation(state.oldAnimation_index)
    state.animation_index = animation_index
    reader.EnableAnimation(state.animation_index)
    state.oldAnimation_index = animation_index
    ctrl.view_update()

# 设置动画速率


@state.change("animation_speed")
def update_animation_speed(animation_speed, **kwargs):
    try:
        if (isinstance(float(animation_speed), float) and float(animation_speed) != 0):
            state.animationSpeed = 100/float(animation_speed)
    except (ValueError):
        print("animation_speed is not float")
    ctrl.view_update()

# 设置动画间隔时间


@state.change("animation_duration")
def update_animation_duration(animation_duration, **kwargs):
    try:
        if (isinstance(float(animation_duration), float) and float(animation_duration) != 0):
            state.animationDuration = float(animation_duration)
    except (ValueError):
        print("animation_duration is not float")
    ctrl.view_update()

# 重置相机视角


def standard_buttons():
    # 两种重置方法
    # with vuetify.VBtn(icon=True, click="$refs.view.resetCamera()"):
    #     vuetify.VIcon("mdi-crop-free")
    with vuetify.VBtn(icon=True, click=restCamera):
        vuetify.VIcon("mdi-crop-free")


def getDevicePixelRatio(devicePixelRatio):
    with state:
        state.devicePixelRatio = devicePixelRatio
    server.js_call("ref_name", "emit", "methodPreventDefault", "参数")


# UI页面
with VAppLayout(server) as layout:
    lableWidth = '160'

    # 注意ClientTriggers的参数命名有要求，不能用xxx_Xxxx的命名方式，否则无法触发
    # 这段代码在多用户的镜像中有问题，需要解决，先注释掉
    # client_triggers = trame.ClientTriggers(
    #     ref="ref_name",
    #     created=(getDevicePixelRatio, "[window.devicePixelRatio]"),
    #     methodPreventDefault="document.oncontextmenu = function(){return false;}",

    # )

    with vuetify.VContainer(
        fluid=True,
        classes="pa-0 fill-height",
        style="position: relative;"
    ):

        view = vtkTrame.VtkRemoteView(renderWindow)
        # view = vtkTrame.VtkRemoteView(
        #     renderWindow, namespace="view", mode="local"
        # )
        ctrl.view_update = view.update

        # fig = vega.Figure(style="position:absolute;top:10vh;right:5vw")
        # ctrl.vega_update = fig.update

        # fig_two = vega.Figure(style="position:absolute;top:50vh;right:5vw")
        # ctrl.vega_update_two = fig_two.update

        with vuetify.VBtnToggle(
            style=("tooltipStyle", {"display": "none"}, {"vertical": "True"}), dense=True, vertical=True
        ):
            with vuetify.VBtn(
                icon=True, click=hide_actor
            ):
                vuetify.VIcon("mdi-eye-off")
            # with vuetify.VBtn(
            #         icon=True, click=clear_all
            #     ):
            #         vuetify.VIcon("mdi-delete")
            with vuetify.VBtn(
                icon=True, click=show_all
            ):
                vuetify.VIcon("mdi-eye-refresh")

        with html.Div(style="position: absolute;top: 20px;left: 20px;width: 400px;background:white;max-height:80vh;overflow:auto"):
            html.Template(
                "<div style='padding-left:24px;padding-top:24px;font-size:20px;color:rgba(0,0,0,.87);padding-bottom:16px'>控制面板</div>")
            html.Template(
                "<div style='padding-left:24px;font-size:16px;color:rgba(0,0,0,.87);padding-bottom:12px'><lable style='width:"+lableWidth+"px;display:inline-block'>点:</lable>{{total_points}}</div>")
            html.Template(
                "<div style='padding-left:24px;font-size:16px;color:rgba(0,0,0,.87);padding-bottom:12px'><lable style='width:"+lableWidth+"px;display:inline-block'>线:</lable>{{total_lines}}</div>")
            html.Template(
                "<div style='padding-left:24px;font-size:16px;color:rgba(0,0,0,.87);padding-bottom:12px'><lable style='width:"+lableWidth+"px;display:inline-block'>面:</lable>{{total_cells}}</div>")
            html.Template(
                "<div style='padding-left:24px;font-size:16px;color:rgba(0,0,0,.87);padding-bottom:12px'><lable style='width:"+lableWidth+"px;display:inline-block'>帧率:</lable> {{current_framerate}}  </div>")
            with vuetify.VExpansionPanels(
                accordion=True,
                style="width: 400px;"
            ):
                with vuetify.VExpansionPanel(
                ):
                    vuetify.VExpansionPanelHeader(
                        '模型列表'
                    )
                    with vuetify.VExpansionPanelContent(
                        # '颜色'
                    ):
                        vuetify.VSelect(
                            items=[
                                ["vtkGLTFImporter", "vtkGLTFReader", "gltfLoader"]],
                            v_model=("selectedReader", "vtkGLTFImporter"),
                        )
                        with vuetify.VRow():
                            with vuetify.VCol(
                                cols=6,
                                sm=6,
                                v_for="(k,index) in modelList",
                                key=("index",),
                                classes=("k.name",),
                                click=(imageClick, "[k, $event]"),
                                style=("k.style", {"font-size": '14px'})
                            ):
                                vuetify.VImg(
                                    src="https://cdn.vuetifyjs.com/images/parallax/material2.jpg"
                                )
                                html.Div("{{k.name}}", key=("index",),)

                with vuetify.VExpansionPanel(
                ):
                    vuetify.VExpansionPanelHeader(
                        '光源'
                    )
                    with vuetify.VExpansionPanelContent(
                        # '颜色'
                    ):
                        vuetify.VTextField(
                            label="x_position",
                            v_model=("x_position", DEFAULT_POSITION),
                        )
                        vuetify.VSlider(
                            v_model=("x_position", DEFAULT_POSITION),
                            min=-8000,
                            max=8000,
                            step=1,
                            label="x坐标",
                            classes="mt-1",
                            hide_details=True,
                            dense=True,
                        )
                        vuetify.VTextField(
                            label="y",
                            v_model=("y_position", DEFAULT_POSITION),
                            value="10"
                        )
                        vuetify.VSlider(
                            # Opacity
                            v_model=("y_position", DEFAULT_POSITION),
                            min=-8000,
                            max=8000,
                            step=1,
                            label="y坐标",
                            classes="mt-1",
                            hide_details=True,
                            dense=True,
                        )
                        vuetify.VTextField(
                            label="z",
                            v_model=("z_position", DEFAULT_POSITION),
                            value="10"
                        )
                        vuetify.VSlider(
                            # Opacity
                            v_model=("z_position", DEFAULT_POSITION),
                            min=-8000,
                            max=8000,
                            step=1,
                            label="z坐标",
                            classes="mt-1",
                            hide_details=True,
                            dense=True,
                        )
                        # cone_angle
                        cone_angle = vuetify.VSlider(
                            # Opacity
                            # v_visible = False,
                            v_model=("coneangle", 1),
                            min=0,
                            max=2,
                            step=0.01,
                            label="张角",
                            style=("coneDisplay", ),
                            hide_details=True,
                            dense=True,
                        )
                        vuetify.VCheckbox(
                            v_model=("open", True),
                            label="关灯"
                        )
                        vuetify.VSelect(
                            label="light type",
                            items=[['平行光', '头灯', '相机灯源', '点光源']],
                            v_model=("light_type", "平行光"),
                        )

                        vuetify.VSlider(
                            # Opacity
                            v_model=("strength", 1),
                            min=0,
                            max=5,
                            step=0.1,
                            label="强度",
                            classes="mt-1",
                            hide_details=True,
                            dense=True,
                        )
                        vuetify.VTextField(
                            label="色温",
                            v_model=("colour_temperature", 1000),
                        )
                        vuetify.VSlider(
                            # Opacity
                            v_model=("colour_temperature", 1000),
                            min=1000,
                            max=40000,
                            step=100,
                            label="色温",
                            classes="mt-1",
                            hide_details=True,
                            dense=True,
                        )
                        vuetify.VColorPicker(
                            v_model=("color", "#FFFFFFFF"),
                            mode="rgba",
                            dot_size=25,
                            swatches_max_height="200",
                            hide_mode_switch=True
                        )
                with vuetify.VExpansionPanel(
                ):
                    with vuetify.VExpansionPanel(
                    ):
                        vuetify.VExpansionPanelHeader(
                            '动画'
                        )
                        with vuetify.VExpansionPanelContent(

                        ):
                            vuetify.VSwitch(
                                label="动画开关",
                                v_model=("animation", False),
                            )

                            vuetify.VSelect(
                                v_show=("showAnimationSelect",),
                                label="动画列表",
                                items=("animationList", [state.animationList]),
                                v_model=("animation_index", 0),
                            )

                            with vuetify.VBtn(icon=True, click=restart_animation, label="重启"):
                                vuetify.VIcon("mdi-autorenew")

                            vuetify.VTextField(
                                label="动画速度",
                                v_model=("animation_speed", 1.0),
                            )

                            vuetify.VTextField(
                                label="播放时长",
                                v_model=("animation_duration", -1.0),
                            )

                standard_buttons()


# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

async def async_server():
    # ctrl.vega_update(chart)
    # ctrl.vega_update_two(chart_two)
    # server.start(host="0.0.0.0", port=8080)
    server.start()


async def main_loop():

    task1 = asyncio.create_task(async_server())
    task2 = asyncio.create_task(async_framerate())
    task3 = asyncio.create_task(async_animation())
    task4 = asyncio.create_task(log_performance())

    await task1
    await task2
    await task3
    await task4

if __name__ == "__main__":

    # 设置相机初始位置和焦点
    camera = renderer.GetActiveCamera()
    initial_position = camera.GetPosition()
    initial_focal_point = camera.GetFocalPoint()
    initial_view_up = camera.GetViewUp()
    restCamera()
    asyncio.run(main_loop())
    # server.start()
